package service

import (
	"context"
	"encoding/json"
	"errors"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	resource "k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"log"
	"strconv"
	"time"
)

var Deployment deployment

type deployment struct{}

//类型转换方法 corev1.Pod -> DataCell ,
func (d *deployment) toCell(deployments []appsv1.Deployment) []DataCell {
	cell := make([]DataCell, len(deployments))
	for i := range deployments {
		cell[i] = deploymentCell(deployments[i])
	}
	return cell
}

//类型转换方法  DataCell -> corev1.Pod
func (d *deployment) fromCells(cells []DataCell) []appsv1.Deployment {
	deployments := make([]appsv1.Deployment, len(cells))
	for i := range cells {
		// cells[i].(deploymentCell) 类型转换
		deployments[i] = appsv1.Deployment(cells[i].(deploymentCell))

	}
	return deployments
}

//定义列表的返回内容 Item是deploy元素列表  Total为元素数量
type DeploymentResp struct {
	Item  []appsv1.Deployment `json:"item"`
	Total int                 `json:"total"`
}

//定义DeployCreate结构体用于创建deployment需要的参数属性的定义
type DeployCreate struct {
	Name          string            `json:"name"`
	Namespace     string            `json:"namespace"`
	Replicas      int32             `json:"replicas"`
	Image         string            `json:"image"`
	Label         map[string]string `json:"label"`
	Cpu           string            `json:"cpu"`
	Memory        string            `json:"memory"`
	ContainerPort int32             `json:"container_port"`
	HealthCheck   bool              `json:"health_check"`
	HealthPath    string            `json:"health_path"`
}

//定义DeploysNp类型 用于返回namespace中deployment的数量
type DeploysNp struct {
	Namespace  string `json:"namespace"`
	DeploysNum int    `json:"deploys_num"`
}

//获取deploy列表 支持过滤 排序 分页
func (d *deployment) GetDeployments(filtername, namespacen string, limit, page int) (deploymentResp *DeploymentResp, err error) {
	//获取deploymentList类型的deploy列表
	deploymentList, err := K8s.ClientSet.AppsV1().Deployments(namespacen).List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		log.Printf("[ Deployment ]资源列表获取失败：%s", err)
		return nil, errors.New("获取Deployment列表失败" + err.Error())
	}

	selectableData := &dataSelector{
		GenericDataList: d.toCell(deploymentList.Items),
		DataSelectQuery: &DataSelectQuery{
			FilterQuery: &FilterQuery{Name: filtername},
			PaginateQuary: &PaginateQuary{
				Limit: limit,
				Page:  page,
			},
		},
	}

	//先过滤
	filtered := selectableData.Filter()
	total := len(filtered.GenericDataList)
	//先排序再分页
	data := filtered.Sort().Paginate()

	deployments := d.fromCells(data.GenericDataList)

	return &DeploymentResp{
		Total: total,
		Item:  deployments,
	}, nil

}

//获取deploy详情
func (d *deployment) GetDeployDetail(deployName, namespace string) (deployment *appsv1.Deployment, err error) {

	deployment, err = K8s.ClientSet.AppsV1().Deployments(namespace).Get(context.TODO(), deployName, metav1.GetOptions{})
	if err != nil {
		log.Printf("获取deployment详情失败：%s", err)
		return nil, errors.New("获取deployment详情失败：%s" + err.Error())
	}
	return deployment, nil
}

//设置deployment副本数
func (d *deployment) ScaleDeployment(deployname, namespace string, scaleNum int32) (replica int32, err error) {
	//获取 类型对象 点出当前副本数
	scale, err := K8s.ClientSet.AppsV1().Deployments(namespace).GetScale(context.TODO(), deployname, metav1.GetOptions{})
	if err != nil {
		log.Printf("获取deployment副本数失败：%s", err)
		return 0, errors.New("获取deployment副本数失败：%s" + err.Error())
	}

	//修改副本数
	scale.Spec.Replicas = int32(scaleNum)
	//更新副本数 传入scale对象
	newScale, err := K8s.ClientSet.AppsV1().Deployments(namespace).UpdateScale(context.TODO(), deployname, scale, metav1.UpdateOptions{})
	if err != nil {
		log.Printf("更新deployment副本数失败：%s", err)
		return 0, errors.New("更新deployment副本数失败：%s" + err.Error())
	}
	return newScale.Spec.Replicas, nil
}

//创建deployment
func (d *deployment) CreateDeployment(data *DeployCreate) (err error) {
	//将data中的数据组装成 appsv1.Deployment 对象
	deployment := &appsv1.Deployment{
		TypeMeta: metav1.TypeMeta{},
		// ObjectMeta中定义资源名，命名空间以及标签
		ObjectMeta: metav1.ObjectMeta{
			Name:      data.Name,
			Namespace: data.Namespace,
			Labels:    data.Label,
		},
		//Spec中定义副本数，选择器以及pod属性
		Spec: appsv1.DeploymentSpec{
			Replicas: &data.Replicas,
			Selector: &metav1.LabelSelector{
				MatchLabels:      data.Label,
				MatchExpressions: nil,
			},
			Template: corev1.PodTemplateSpec{
				//定义pod名和标签
				ObjectMeta: metav1.ObjectMeta{
					Name:   data.Name,
					Labels: data.Label,
				},
				//定义容器名 镜像和端口
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name:  data.Name,
							Image: data.Image,
							Ports: []corev1.ContainerPort{
								{
									Name:          "http",
									Protocol:      corev1.ProtocolTCP,
									ContainerPort: 80,
								},
							},
						},
					},
				},
			},
		},
		//Status定义资源的运行状态 新建传空即可
		Status: appsv1.DeploymentStatus{},
	}
	//判断是否打开健康检查，若打开 则定义ReadinessProbe和LivenessProbe
	if data.HealthCheck {
		//设置第一个容器的ReadinessProbe 因为这里pod只有一个容器  所以使用Index[0],若有多个容器  则使用for循环定义
		deployment.Spec.Template.Spec.Containers[0].ReadinessProbe = &corev1.Probe{
			Handler: corev1.Handler{
				HTTPGet: &corev1.HTTPGetAction{
					Path: data.HealthPath,
					Port: intstr.IntOrString{
						//intstr.IntOrString的端口可以定义为整形 也可为字符串
						//Type: 0表示为整型 转json时使用IntVal的数据   type=1时为字符串 转json是使用strval的数据
						Type:   0,
						IntVal: data.ContainerPort,
					},
					Host:        "",
					Scheme:      "",
					HTTPHeaders: nil,
				},
			},
			//初始化等待时间
			InitialDelaySeconds: 10,
			//超时时间
			TimeoutSeconds: 10,
			//执行间隔
			PeriodSeconds:    5,
			//连续探测几次成功或失败
			SuccessThreshold: 0,
			FailureThreshold: 0,
		}
	}
	// 定义容器的limit和request资源
	deployment.Spec.Template.Spec.Containers[0].Resources.Limits = map[corev1.ResourceName]resource.Quantity{
		corev1.ResourceCPU:    resource.MustParse(data.Cpu),
		corev1.ResourceMemory: resource.MustParse(data.Memory),
	}
	deployment.Spec.Template.Spec.Containers[0].Resources.Requests = map[corev1.ResourceName]resource.Quantity{
		corev1.ResourceCPU:    resource.MustParse(data.Cpu),
		corev1.ResourceMemory: resource.MustParse(data.Memory),
	}
	//调用sdk创建deployment
	_, err = K8s.ClientSet.AppsV1().Deployments(data.Namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
	if err != nil {
		log.Printf("创建deployment失败：%s", err)
		return errors.New("创建deployment失败：%s" + err.Error())
	}
	return nil
}

//删除deployment
func (d *deployment) DeleteDeploy(deployname, namespace string) (err error) {
	err = K8s.ClientSet.AppsV1().Deployments(namespace).Delete(context.TODO(), deployname, metav1.DeleteOptions{})
	if err != nil {
		log.Printf("删除DeleteDeploy失败：%s", err)
		return errors.New("删除DeleteDeploy失败:" + err.Error())
	}
	return nil
}

//重启deployment 或者可以使用scale设置副本为0在改回原来值
func (d *deployment) ReStartDeploy(deployname, namespace string) (err error) {
	//此功能同等于kubectl deploy ${service} -p '{"spec":{"template":{"spec":{"container":[{"name":"'"${service}"'","env":[{"name":"RESTART_","value":"'$(date +%s)'"}]}]}}}}'
	//使用patchData Map组装数据
	patchDate := map[string]interface{}{
		"spec": map[string]interface{}{
			"template": map[string]interface{}{
				"spec": map[string]interface{}{
					"containers": []map[string]interface{}{
						{
							"name": deployname,
							"env": []map[string]string{{
								"name":  "RESTART_",
								"value": strconv.FormatInt(time.Now().Unix(), 10),
							}},
						},
					},
				},
			},
		},
	}
	//序列化为字节 patch方法只接收字节
	patchByte, err := json.Marshal(patchDate)
	if err != nil {
		log.Printf("json序列化失败：%s", err)
		return errors.New("json序列化失败:" + err.Error())
	}
	//调用patch方法更新deploy
	_,err = K8s.ClientSet.AppsV1().Deployments(namespace).Patch(context.TODO(),deployname,
		"application/strategic-merge-patch+json",patchByte,metav1.PatchOptions{})
	if err != nil {
		log.Printf("DeleteDeploy重启失败：%s", err)
		return errors.New("DeleteDeploy重启失败:" + err.Error())
	}
	return nil
}


//更新deployment
//content参数是请求中传入的pod对象的json数据
func (d *deployment) UpDateDeploy( namespace, content string) (err error) {
	var deploy = &appsv1.Deployment{}
	//反序列化为pod对象
	err = json.Unmarshal([]byte(content), deploy)
	if err != nil {
		log.Printf("反序列化失败：%s", err)
		return errors.New("反序列化失败:" + err.Error())
	}
	//更新pod
	_, err = K8s.ClientSet.AppsV1().Deployments(namespace).Update(context.TODO(), deploy, metav1.UpdateOptions{})
	if err != nil {
		log.Printf("deployment 更新失败：%s", err)
		return errors.New("deployment 更新失败:" + err.Error())
	}
	return nil
}


type DeployNp struct {
	Namespace string
	DeployNum int
}


//获取每个namespace中的deploy数量
func (d *deployment) GetDeployNumPerNp() (deployNps []*DeployNp,err error){
	//获取namespace列表
	namespaceList, err := K8s.ClientSet.CoreV1().Namespaces().List(context.TODO(),metav1.ListOptions{})
	if err != nil {
		return nil,err
	}
	for _ ,namespace := range namespaceList.Items{
		//获取deploy列表
		deployList, err := K8s.ClientSet.CoreV1().Pods(namespace.Name).List(context.TODO(),metav1.ListOptions{})
		if err != nil {
			return nil,err
		}
		//组装数据
		deployNp := &DeployNp{
			Namespace: namespace.Name,
			DeployNum: len(deployList.Items),
		}
		//添加到deploysNps数组中
		deployNps = append(deployNps,deployNp)
	}
	return deployNps,nil
}